package com.master.controller;

import com.master.access.AccessLimit;
import com.master.config.HostHolder;
import com.master.domain.MiaoshaOrder;
import com.master.domain.MiaoshaUser;
import com.master.domain.OrderInfo;
import com.master.rabbitmq.MQSender;
import com.master.rabbitmq.MiaoshaMessage;
import com.master.redis.AccessKey;
import com.master.redis.GoodsKey;
import com.master.redis.MiaoshaKey;
import com.master.redis.RedisServer;
import com.master.service.GoodsService;
import com.master.service.MiaoshaService;
import com.master.service.OrderService;
import com.master.util.CodeMsg;
import com.master.util.MD5Util;
import com.master.util.Result;
import com.master.util.UUIDUtil;
import com.master.vo.GoodsVo;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/miaosha")
public class MiaoshaController implements InitializingBean{

    @Autowired
    RedisServer redisServer;

    @Autowired
    GoodsService goodsService;

    @Autowired
    OrderService orderService;

    @Autowired
    MiaoshaService miaoshaService;

    @Autowired
    MQSender mqSender;

    @Autowired
    HostHolder hostHolder;

    private Map<Long,Boolean> localOverMap = new HashMap<>();



    /***
     * GET请求是幂等的,代表从服务端获取数据,无论执行多少次GET请求,都不会对服务器端数据产生影响
     * POST请求不是幂等的,调用POST请求,向服务端提交数据,会对服务端数据的变化产生影响
     * @param model
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "/{path}/do_miaosha",method = RequestMethod.POST)
    @ResponseBody
    public Result<Integer> miaosha(Model model, @RequestParam("goodsId")long goodsId,
                                   @PathVariable("path")String path) {
        MiaoshaUser user = hostHolder.getUser();
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        //需要验证path
        boolean check = miaoshaService.checkPath(path,user,goodsId);
        if(check == false){
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }

        /**
        //判断商品库存
        GoodsVo goodsVo = goodsService.getGoodsVoByGoodsId(goodsId);
        int stock = goodsVo.getStockCount();//获取的是秒杀商品的库存
        if (stock <= 0) {
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }
        //判断是否已经秒杀到了
        //查询秒杀订单,如果可以查询到对应的秒杀订单,则说明用户已经秒杀过了
        MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
        if(order != null){
            return Result.error(CodeMsg.REPEATE_MIAO_SHA);
        }

        //秒杀步骤
        //1.减库存
        //2.下订单
        //3.写入秒杀订单
        OrderInfo orderInfo = miaoshaService.miaosha(user,goodsVo);//秒杀成功之后返回订单页
        return Result.success(orderInfo);//将订单返回
         */

        //先从Map中查询goodsId对应的商品是否秒杀结束
        //用做一个内存的标记,减少对redis的访问
        boolean over = localOverMap.get(goodsId);
        if(over){
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }

        //用户请求过来时,不直接去访问数据库,而是先去redis中查询缓存,预减库存
        //返回的是已经从redis中减去的1之后的库存数量
        long stock = redisServer.decr(GoodsKey.getMiaoshaGoodsStock,""+goodsId);
        if(stock < 0){
            //如果减去之后库存小于0,则秒杀失败
            localOverMap.put(goodsId,true);
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }

        //判断用户是否已经秒杀
        MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
        if(order != null){
            //用户已经秒杀过了
            return Result.error(CodeMsg.REPEATE_MIAO_SHA);
        }

        //入队
        //将用户秒杀的信息存储到消息中间件中
        MiaoshaMessage message = new MiaoshaMessage();
        message.setUser(user);
        message.setGoodsId(goodsId);
        mqSender.sendMiaoshaMessage(message);

        //这块已经将MiaoshaMessage方法消息队列中,还未生成订单
        //应该返回排队中
        return Result.success(0);//排队中
    }


    /**
     * 系统初始化
     * 从数据库中查询商品加载到redis中
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        //从数据库中查询出所有秒杀商品List
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        if(goodsList == null){
            return;
        }
        //如果商品不为null,将秒杀商品加载到redis中
        //在系统启动时将秒杀商品加载到redis中
        for(GoodsVo  goods : goodsList){
            //将每个商品的库存数量缓存进redis中
            redisServer.set(GoodsKey.getMiaoshaGoodsStock,""+goods.getId(),goods.getStockCount());
            //在系统初始化时,将商品id做了一个标记,代表商品秒杀没有结束
            localOverMap.put(goods.getId(),false);
        }
    }

    /**
     * 如果秒杀成功,返回orderId(订单ID)
     * 库存不足,返回-1
     * 秒杀失败返回-1
     *
     * 排队中返回0
     * @param model
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "/result",method = RequestMethod.GET)
    @ResponseBody
    public Result<Long> miaoshaResult(Model model,
                                         @RequestParam("goodsId")long goodsId){
        MiaoshaUser user = hostHolder.getUser();
        if(user == null){
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        //查询是否生成订单
        long result = miaoshaService.getMiaoshaResult(user.getId(),goodsId);
        return Result.success(result);
    }

    //生成秒杀的path
    @AccessLimit(seconds = 5,maxCount = 5)
    @RequestMapping(value = "/path",method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getMiaoshaPath(@RequestParam("goodsId")long goodsId,
                                         @RequestParam(value = "verifyCode",defaultValue = "0")int verifyCode, HttpServletRequest request){
        MiaoshaUser user = hostHolder.getUser();
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

//        String uri = request.getRequestURI();
//        String key = uri + "_" + user.getId();
//        //查询访问的次数
//        //限制5秒钟访问5次
//        Integer count = redisServer.get(AccessKey.access,key,Integer.class);
//        if(count == null){
//            //表明还没有访问过
//            redisServer.set(AccessKey.access,key,1);
//        }else if(count < 5){
//            //如果访问次数小于5次,直接增加一次访问次数
//            redisServer.incr(AccessKey.access,key);
//        }else{
//            //访问过于频繁
//            return Result.error(CodeMsg.ACCESS_LIMIT_REACHE);
//        }

        //对验证码做校验
        boolean check = miaoshaService.checkVerifyCode(user,goodsId,verifyCode);
        if(check == false){
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }

        String path = miaoshaService.createMiaoshaPath(user,goodsId);
        return Result.success(path);
    }

    @RequestMapping(value = "/verifyCode",method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getMiaoshaVerifyCode(@RequestParam("goodsId")long goodsId,
                                               HttpServletResponse response){
        MiaoshaUser user = hostHolder.getUser();
        if(user == null){
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        BufferedImage image = miaoshaService.createMiaoshaVerifyCode(user,goodsId);
        try{
            OutputStream out = response.getOutputStream();
            //将图片写入输入流
            ImageIO.write(image,"JPEG",out);
            out.flush();
            out.close();
        }catch (IOException e) {
            e.printStackTrace();
            return Result.error(CodeMsg.MIAOSHA_FAIL);
        }
        return null;
    }
}
